<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="qgTMp" id="qgTMp"><span data-lake-id="u71847a2e" id="u71847a2e">典型回答</span></h1>
  <p data-lake-id="u64f01d9f" id="u64f01d9f"><span data-lake-id="u28a77b46" id="u28a77b46">Java泛型（generics） 是JDK 5中引入的一个新特性，允许在定义类和接口的时候使用类型参数（type parameter）。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。</span></p>
  <p data-lake-id="u2d395d67" id="u2d395d67"><span data-lake-id="u8c4979c7" id="u8c4979c7">​</span><br></p>
  <p data-lake-id="u6e257fef" id="u6e257fef"><span data-lake-id="ufd5fa2d4" id="ufd5fa2d4">泛型的好处有两个：</span></p>
  <ol list="u98de003c">
   <li fid="u968f2b1f" data-lake-id="u8d54fe8a" id="u8d54fe8a"><span data-lake-id="u846f6a42" id="u846f6a42">方便：可以提高代码的复用性。以List接口为例，我们可以将String、Integer等类型放入List中，如不用泛型，存放String类型要写一个List接口，存放Integer要写另外一个List接口，泛型可以很好的解决这个问题</span></li>
   <li fid="u968f2b1f" data-lake-id="uaab7a5df" id="uaab7a5df"><span data-lake-id="u45431915" id="u45431915">安全：在泛型出之前，通过Object实现的类型转换需要在运行时检查，如果类型转换出错，程序直接GG，可能会带来毁灭性打击。而泛型的作用就是在编译时做类型检查，这无疑增加程序的安全性</span></li>
  </ol>
  <h1 data-lake-id="RgiyV" id="RgiyV"><span data-lake-id="u6c890eb6" id="u6c890eb6">知识扩展</span></h1>
  <h2 data-lake-id="NJ7rO" id="NJ7rO"><span data-lake-id="u45f8ec5b" id="u45f8ec5b">泛型是如何实现的</span></h2>
  <p data-lake-id="u43415247" id="u43415247"><span data-lake-id="u8ea130fc" id="u8ea130fc">Java中的泛型通过</span><strong><span data-lake-id="uc9cab715" id="uc9cab715">类型擦除</span></strong><span data-lake-id="u3bf427c3" id="u3bf427c3">的方式来实现，通俗点理解，就是通过语法糖的形式，在.java-&gt;.class转换的阶段，将List&lt;String&gt;擦除调转为List的手段。换句话说，Java的泛型只在编译期，Jvm是不会感知到泛型的。</span></p>
  <p data-lake-id="u1cc3d79c" id="u1cc3d79c"><span data-lake-id="u38d76835" id="u38d76835">​</span><br></p>
  <p data-lake-id="uac917aac" id="uac917aac"><br></p>
  <h2 data-lake-id="iXKzC" id="iXKzC"><span data-lake-id="u27b8ef77" id="u27b8ef77">类型擦除的缺点有哪些？</span></h2>
  <ol list="udfe96e91">
   <li fid="u7fba57cb" data-lake-id="uecca2965" id="uecca2965"><span data-lake-id="u32f98f60" id="u32f98f60">泛型不可以重载</span></li>
   <li fid="u7fba57cb" data-lake-id="u70770349" id="u70770349"><span data-lake-id="uf9a5ddaa" id="uf9a5ddaa">泛型异常类不可以多次catch</span></li>
   <li fid="u7fba57cb" data-lake-id="u5a8c4db1" id="u5a8c4db1"><span data-lake-id="ucb1325b0" id="ucb1325b0">泛型类中的静态变量也只有一份，不会有多份</span></li>
  </ol>
  <h2 data-lake-id="Heghh" id="Heghh"><span data-lake-id="u64f49294" id="u64f49294">对泛型通配符的理解</span></h2>
  <h2 data-lake-id="jjq9K" id="jjq9K"><span data-lake-id="u78bb74fd" id="u78bb74fd">List&lt;?&gt;, List&lt;Object&gt;, List之间的区别</span></h2>
  <ol list="u9f6fcb10">
   <li fid="u23d99ad7" data-lake-id="u66cd1480" id="u66cd1480"><span data-lake-id="ued55d576" id="ued55d576">List&lt;?&gt; 是一个未知类型的List，而List&lt;Object&gt; 其实是任意类型的List。可以把List&lt;String&gt;, List&lt;Integer&gt;赋值给List&lt;?&gt;，却不能把List&lt;String&gt;赋值给 List&lt;Object&gt;</span></li>
   <li fid="u23d99ad7" data-lake-id="ua377c22d" id="ua377c22d"><span data-lake-id="ubaacc89e" id="ubaacc89e">可以把任何带参数的类型传递给原始类型List，但却不能把List&lt;String&gt;赋值给List&lt;Object&gt;，因为会产生编译错误（不支持协变）</span></li>
   <li fid="u23d99ad7" data-lake-id="u16219bc6" id="u16219bc6"><span data-lake-id="u80fe6766" id="u80fe6766">List&lt;?&gt;由于不确定列表中元素的具体类型，因此只能从这种列表中读取数据，而不能往里面添加除了 null 之外的任何元素。</span></li>
  </ol>
  <p data-lake-id="ue57f727c" id="ue57f727c"><span data-lake-id="uc5b68b5b" id="uc5b68b5b">​</span><br></p>
  <p data-lake-id="u42c0a6ca" id="u42c0a6ca" style="text-align: center"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1703322631480-b31e952c-b076-48ee-b01a-2a56c65c9a5c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_17%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u7deb7c81" id="u7deb7c81"><br></p>
  <h2 data-lake-id="gELmY" id="gELmY"><span data-lake-id="u024d97b6" id="u024d97b6">在泛型为Integer的ArrayList中存放一个String类型的对象</span></h2>
  <p data-lake-id="ud2af0776" id="ud2af0776"><br></p>
  <p data-lake-id="uad6b7353" id="uad6b7353"><span data-lake-id="ud87ee3d0" id="ud87ee3d0">通过反射可以实现：</span></p>
  <p data-lake-id="uf9304ed6" id="uf9304ed6"><span data-lake-id="u1289c9a4" id="u1289c9a4">​</span><br></p>
  <pre lang="java"><code>
public void test() throws Exception {
    ArrayList&lt;Integer&gt; list = new ArrayList&lt;Integer&gt;();
    Method method = list.getClass().getMethod("add", Object.class);
    method.invoke(list, "Java反射机制实例");
    System.out.println(list.get(0));
}
</code></pre>
  <h2 data-lake-id="VWeKD" id="VWeKD"><br></h2>
  <h2 data-lake-id="J2Iy1" id="J2Iy1"><span data-lake-id="u7197bf49" id="u7197bf49">对数组协变和泛型非协变的理解</span></h2>
  <p data-lake-id="u9250bd77" id="u9250bd77"><span data-lake-id="u38c86119" id="u38c86119">所谓协变，可以简单理解为因为Object是String的父类，所以Object[]同样是String[]的父类，这种情况Java是允许的；但是对于泛型来说，List&lt;Object&gt;和List&lt;String&gt;半毛钱关系都没有</span></p>
  <p data-lake-id="ue461539c" id="ue461539c"><span data-lake-id="u34272da6" id="u34272da6">为什么要这样设计呢，如果泛型允许协变（实际上以下代码第一步就会编译失败），考虑如下例子：</span></p>
  <pre lang="java"><code>
List&lt;Object&gt; a = new List&lt;String&gt;();
a.add(1); // 允许协变，可以装进来
String s = a.get(0); // 编译报错
</code></pre>
  <p data-lake-id="u7d320696" id="u7d320696"><span data-lake-id="u3a068d83" id="u3a068d83">但是，为什么泛型不允许协变，而数组允许协变呢？原因有二：</span></p>
  <ol list="ud385935a">
   <li fid="u75d7573e" data-lake-id="u4871ebab" id="u4871ebab"><span data-lake-id="u5e0b8845" id="u5e0b8845">因为数组设计之初没有泛型，为了兼容考虑，如</span><code data-lake-id="u5297b818" id="u5297b818"><span data-lake-id="ub62b5e70" id="ub62b5e70">Arrays.equals(Object[], Object[])</span></code><span data-lake-id="u8afd73da" id="u8afd73da">方法，是时代无奈的产物</span></li>
   <li fid="u75d7573e" data-lake-id="u90ba17e0" id="u90ba17e0"><span data-lake-id="ud7e07d31" id="ud7e07d31">数组也属于对象，它记录了引用实际的类型，在放入数组的时候，如果类型不一样就会报错，而不是等到拿出来的时候才发现问题，相对来说安全一点</span></li>
  </ol>
 </body>
</html>